import sys
import random
from time import time
sys.path.append('./pypoker-eval-137.0')
sys.path.append('./pypoker-eval-137.0/.libs')
from pokereval import PokerEval

start = 0
pokereval = PokerEval()
file = open('preflopranks.txt','r')
data = file.read()
A=[float(i) for i in str(data).split('\t')]
P = [A[13*i:13*i+13] for i in range(13)]
MY_PREFLOP_AGGRO, MY_PREFLOP_TIGHT, MY_POSTFLOP_AGGRO, MY_POSTFLOP_TIGHT, MY_SLOWPLAY, MY_BLUFF, MY_BIG_BET = 0.25, 0.6, 0.45, 0.7, 0.1, 0.15, 0.5
OPP_PREFLOP_AGGRO, OPP_PREFLOP_TIGHT, OPP_POSTFLOP_AGGRO, OPP_POSTFLOP_TIGHT, OPP_SLOWPLAY, OPP_BLUFF = 0.35, 0.65, 0.35, 0.5, 0.2, 0.2

def startTimer():
    global start
    start = time()

def elapsed():
    return time() - start

class Card():
    ranks = [2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K', 'A']
    suits = ['clubs', 'diamonds', 'hearts', 'spades']

    def __init__(self, id):
        self.id = id
        self.rank = id % 13
        self.suit = id / 13

    def compareTo(self, card):
        return cmp(self.rank, card.rank)

    def equals(self, card):
	    return self.id == card.id

    def write(self):
        print Card.ranks[self.rank], 'of', Card.suits[self.suit]

class Hand():
    def __init__(self, cards):
        self.cards = cards
	
    # Adds the given cards to the hand
    def add(self, cards):
        self.cards.extend(cards)
	
    # Returns combination of current and given hands, leaving originals unchanged
    def combine(self, hand):
        copy = list(self.cards)
        copy.extend(hand.cards)
        return Hand(copy)

    def size(self):
        return len(self.cards)

    def rankCount(self):
        rankCount = [0] * 13
        for i in self.cards:
            rankCount[i.rank] += 1
        return rankCount

    def suited(self):
        for i in self.cards[1:]:
            if i.suit != self.cards[0].suit:
                return False
        return True

    def write(self):
        for card in self.cards:
            card.write()

class Deck():
    cards = [1] * 52
    size = 52

    @staticmethod
    def add(card):
        if Deck.cards[card] == 0:
            Deck.cards[card] = 1
            Deck.size += 1

    @staticmethod
    def addAll(hand):
        for card in hand.cards:
            Deck.add(card.id)
	
    @staticmethod
    def remove(card):
        if Deck.cards[card] == 1:
            Deck.cards[card] = 0
            Deck.size -= 1
            return True
        else:
            return False

    @staticmethod
    def removeAll(hand):
        for card in hand.cards:
            Deck.remove(card.id)

    @staticmethod
    def contains(card):
        return Deck.cards[card] == 1

    @staticmethod
    def deal(n):
        hand = Hand([])
        for i in range(n):
            card = Card(random.randint(0, 51))
            while Deck.cards[card.id] == 0:
                card = Card(random.randint(0, 51))
            Deck.remove(card.id)
            hand.add([card])
        return hand

    @staticmethod
    def shuffle():
        Deck.cards = [1] * 52
        Deck.size = 52

class Pot():
    size = 0
    bets = {}
    raiseCounts = {}
    minRaiseAmt = 2

    @staticmethod
    def add(player, bet):
        Pot.minRaiseAmt = max(Pot.minRaiseAmt, Pot.bets[player] + bet - max(Pot.bets.values()))
        Pot.size += bet
        Pot.bets[player] += bet

    @staticmethod
    def setSize(size):
        Pot.size = size

    @staticmethod
    def toCall(player):
        return max(Pot.bets.values()) - Pot.bets[player]

    @staticmethod
    def newGame():
        Pot.size = 0
        Pot.bets = {Player.me: 0, Player.leftOpp: 0, Player.rightOpp: 0}
        Pot.raiseCounts = {Player.me: 0, Player.leftOpp: 0, Player.rightOpp: 0}
        Pot.minRaiseAmt = Game.bb

    @staticmethod
    def newHand():
        Pot.size = 0
        Pot.bets = {Player.me: 0, Player.leftOpp: 0, Player.rightOpp: 0}
        Pot.raiseCounts = {Player.me: 0, Player.leftOpp: 0, Player.rightOpp: 0}
        Pot.minRaiseAmt = Game.bb

    @staticmethod
    def newStreet():
        Pot.bets = {Player.me: 0, Player.leftOpp: 0, Player.rightOpp: 0}
        Pot.raiseCounts = {Player.me: 0, Player.leftOpp: 0, Player.rightOpp: 0}
        Pot.minRaiseAmt = Game.bb
        
class Action():
    FOLD, CALL, RAISE, POST, DEAL, SHOW, WIN, OTHER = 0, 1, 2, 3, 4, 5, 6, 7
    def __init__(self, action):
        spl = action.split(":")
        self.player = Player.getPlayer(spl[1])
        self.type = getattr(self, self.mapType(spl[0]))
        if len(spl) == 3:
            self.val = int(spl[2])
        elif len(spl) == 4:
            self.val = Hand([parseCard(spl[2]), parseCard(spl[3])])
        else:
            self.val = None
        if self.type == Action.CALL:
            self.val = Pot.toCall(self.player)
    
    def mapType(self, type):
        if type == "FOLD" or type == "CALL" or type == "RAISE" or type == "POST" or type == "DEAL" or type == "WIN":
            return type
        elif type == "CHECK":
            return "CALL"
        elif type == "BET":
            return "RAISE"
        elif type == "SHOWS":
            return "SHOW"
        else:
            return "OTHER"

    def perform(self):
        if self.type == Action.FOLD:
            self.player.folded = True
            self.player.foldedCount += 1
            self.player.dist = [0] * 51
            if self.player == Player.leftOpp:
                Player.me.adjHS1 = 0
            elif self.player == Player.rightOpp:
                Player.me.adjHS2 = 0
            Game.playersLeft -= 1
        elif self.type == Action.CALL:
            self.player.bet(Pot.toCall(self.player))
        elif self.type == Action.RAISE:
            self.player.bet(self.val - Pot.bets[self.player])
            Pot.raiseCounts[self.player] += 1
        elif self.type == Action.POST:
            self.player.bet(self.val)
        elif self.type == Action.DEAL:
            Game.newStreet()
        elif self.type == Action.SHOW:
            Game.street = 4
            self.player.hand = self.val
        elif self.type == Action.WIN:
            self.player.winCount += 1
        else:
            pass
        if self.player is not None:
            self.player.actions[Game.street].append(self)

    def isType(self, type):
        return self.type == getattr(self, type)

class Player():
    me, leftOpp, rightOpp = None, None, None
    def __init__(self, name):
        self.name = name
        self.position = 0
        self.hand = Hand([])
        self.stack = 200
        self.actions = [[] for i in range(5)]
        self.time = 10000
        self.folded = False
        self.preflopAggro, self.preflopTight = OPP_PREFLOP_AGGRO, OPP_PREFLOP_TIGHT
        self.postflopAggro, self.postflopTight = OPP_POSTFLOP_AGGRO, OPP_POSTFLOP_TIGHT
        self.slowplay = OPP_SLOWPLAY
        self.bluff = OPP_BLUFF
        self.bigBet = MY_BIG_BET
        self.largestBet = 0
        self.winCount, self.foldedCount, self.sawFlopCount, self.bigBetCount = 0, 0, 0, 0
        self.winPct, self.foldedPct, self.sawFlopPct, self.bigBetPct = 0, 0, 0, 0
        self.dist = [0] * 51
        self.preflopActions, self.postflopActions = [], []
        self.preflopStrengths, self.postflopStrengths = [], []
        self.absHS, self.adjHS1, self.adjHS2, self.draws = 0, 0, 0, 0

    def newHand(self, data = None):
        if self == Player.me:
            self.position = int(data[2])
            Player.leftOpp.position = (self.position + 1) % 3
            Player.rightOpp.position = (self.position + 2) % 3
            self.hand = Hand([parseCard(data[3]), parseCard(data[4])])
            self.stack = Game.maxStack
            self.actions = [[] for i in range(5)]
            self.folded = False
            self.largestBet = 0
            self.winPct, self.foldedPct, self.sawFlopPct, self.bigBetPct = float(self.winCount) / Game.handId, float(self.foldedCount) / Game.handId, float(self.sawFlopCount) / Game.handId, float(self.bigBetCount) / Game.handId
            self.dist = [0] * 51
            self.absHS, self.adjHS1, self.adjHS2, self.draws = 0, 0, 0, 0
        else:
            self.hand = Hand([])
            self.stack = Game.maxStack
            self.actions = [[] for i in range(5)]
            self.folded = False
            self.largestBet = 0
            self.winPct, self.foldedPct, self.sawFlopPct, self.bigBetPct = float(self.winCount) / Game.handId, float(self.foldedCount) / Game.handId, float(self.sawFlopCount) / Game.handId, float(self.bigBetCount) / Game.handId
            self.dist = [0] * 51
            self.absHS, self.adjHS1, self.adjHS2, self.draws = 0, 0, 0, 0

    def bet(self, amt):
        if amt > self.largestBet:
            self.largestBet = amt
        if amt > 100:
            self.bigBetCount += 1
        elif amt > 70:
            self.bigBetCount += 0.5
        Pot.add(self, amt)
        self.stack -= amt

    # Get player with given name
    @staticmethod
    def getPlayer(name):
        if name == Player.me.name:
            return Player.me
        elif name == Player.leftOpp.name:
            return Player.leftOpp
        elif name == Player.rightOpp.name:
            return Player.rightOpp
        elif name is not None and name not in ['FLOP', 'TURN', 'RIVER']:
            return Player.me
        else:
            return None

class Game():
    # Game variables
    gameId, numHands, maxStack, bb, sb, maxTime = 1, 100, 200, 2, 1, 10000

    # Hand variables
    handId, community, playersLeft = 2, Hand([]), 3

    # Street variables
    street, lastStreet = 0, -1

    @staticmethod
    def newGame(data):
        Game.gameId = int(data[1])
        Player.me = Player('FlipFlop' + str(sys.argv[1]))
        Player.me.preflopAggro, Player.me.preflopTight, Player.me.postflopAggro, Player.me.postflopTight, Player.me.slowplay, Player.me.bluff = MY_PREFLOP_AGGRO, MY_PREFLOP_TIGHT, MY_POSTFLOP_AGGRO, MY_POSTFLOP_TIGHT, MY_SLOWPLAY, MY_BLUFF
        Player.leftOpp = Player(data[2])
        Player.rightOpp = Player(data[3])
        Pot.newGame()
        Game.numHands, Game.maxStack, Game.bb, Game.sb, Game.maxTime = int(data[4]), int(data[5]), int(data[6]), int(data[7]), float(data[8])

    @staticmethod
    def newHand(data):
        Game.handId, Game.community, Game.playersLeft, Game.street, Game.lastStreet = int(data[1]), Hand([]), 2, 0, -1
        Player.me.newHand(data)
        Player.leftOpp.newHand()
        Player.rightOpp.newHand()
        Pot.newHand()

    @staticmethod
    def newStreet():
        Game.street += 1
        Pot.newStreet()

# Raise prob, call prob, fold prob
def smarty(handDesc):
    raiseProb = 0
    return normalize([max(.8 - handDesc[1], 0), handDesc[1] - Player.me.toCall() * handDesc[1] ** 2, Player.me.toCall() * handDesc[1] ** 2])

def normalize(probs):
    n = sum(probs)
    probs = [probs[i] / n for i in range(len(probs))]

# Scale win percentage after flop to percentile (est. lowest ev is 157 and highest ev is 816)
def flopScaleWin(pct):
    a = (816 - 157) / 1077.
    b = 157.
    percentile = 1 - (pct - b) / (1077 * a)
    if percentile < 0:
        return 0
    elif percentile > 1:
        return 1
    return percentile

# Scale win percentage after turn to percentile (est. lowest ev 098 is and highest ev is 868)
def turnScaleWin(pct):
    a = (868 - 98) / 1033.
    b = 98.
    percentile = 1 - (pct - b) / (1033 * a)
    if percentile < 0:
        return 0
    elif percentile > 1:
        return 1
    return percentile

def parseEval(data):
    return data['eval'][0]['ev']

def parseCard(s):
    ranks = ['2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A']
    suits = ['c', 'd', 'h', 's']
    return Card(ranks.index(s[0].upper()) + 13 * suits.index(s[1].lower()))

def makeHand(cards):
    hand = Hand([])
    for card in cards:
        hand.add([parseCard(card)])
    Deck.removeAll(hand)
    return hand

def tester():
    Game.newGame('NEWGAME 1 testbot3001 testbot3002 100 200 2 1 10000.00000'.split(' '))
    Game.newHand('NEWHAND 1 0 Ac 9d 0 0 0 10000.00000'.split(' '))
